/*******************************************************************************
 * Copyright (c) 2011 EclipseDevX.org and others.
 * 
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the Eclipse Public License v1.0 which
 * accompanies this distribution, and is available at:
 * 
 *     http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/

package org.eclipsedevx.appengine.usage;

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.channels.Channels;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.SortDirection;
import com.google.appengine.api.files.AppEngineFile;
import com.google.appengine.api.files.FileService;
import com.google.appengine.api.files.FileServiceFactory;
import com.google.appengine.api.files.FileWriteChannel;

@SuppressWarnings("serial")
public class GenerateReportServlet extends HttpServlet {
	private static final Logger log = Logger.getLogger(GenerateReportServlet.class.getName());
	
	public GenerateReportServlet() {
		
	}
	
	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws IOException {
		List<User> users = collectData();
		
		String reportData = createReportData(users);
		
		if (isCronJob(req) || !blobExists(ReportGenerator.FILE_NAME)) {
			if (storeReportData(reportData)) {
				resp.setContentType("text/plain");
				resp.getWriter().println("[ok]");
				resp.getWriter().close();
			} else {
				resp.setContentType("text/plain");
				resp.getWriter().println("[-]");
				resp.getWriter().close();
			}
		} else {
			resp.setContentType("text/html");
			resp.getWriter().println(reportData);
			resp.getWriter().close();
		}
	}

	private boolean blobExists(String fileName) {
		return BlobUtil.getBlobNamed(fileName) != null;
	}

	private boolean isCronJob(HttpServletRequest req) {
		// X-AppEngine-Cron: true
		
		return "true".equals(req.getHeader("X-AppEngine-Cron"));
	}

	private String createReportData(List<User> users) {
		return new ReportGenerator(users).generateReport();
	}

	private boolean storeReportData(String reportData) {
		FileService fileService = FileServiceFactory.getFileService();
		BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
		
		BlobKey blobKey = BlobUtil.getBlobNamed(ReportGenerator.FILE_NAME);
		
		if (blobKey != null) {
			blobstoreService.delete(blobKey);
		}

		try {
			AppEngineFile file = fileService.createNewBlobFile("text/html", ReportGenerator.FILE_NAME);
			
			FileWriteChannel outChannel = fileService.openWriteChannel(file, true);
			
			BufferedWriter writer = new BufferedWriter(Channels.newWriter(outChannel, "UTF8"));
			
			writer.write(reportData);
			
			writer.close();
			
			outChannel.closeFinally();
			
			return true;
		} catch (IOException ioe) {
			log.log(Level.SEVERE, ioe.getMessage(), ioe);
			
			return false;
		}
	}

	private List<User> collectData() {
		Map<String, User> userMap = new HashMap<String, User>();
		
		DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

		Query q = new Query("UsageData");
		
		q.addSort("upload.time", SortDirection.DESCENDING);
		q.addFilter("upload.time", Query.FilterOperator.GREATER_THAN_OR_EQUAL, getDate1WeekAgo());

		PreparedQuery pq = datastore.prepare(q);

		for (Entity entity : pq.asIterable()) {
		  String machineId = (String)entity.getProperty("machine.id");
		  
		  if (userMap.get(machineId) == null) {
			  User user = new User(entity);
			  
			  userMap.put(machineId, user);
		  }
		}
		
		return new ArrayList<User>(userMap.values());
	}
	
	private Date getDate1WeekAgo() {
		Calendar cal = DateUtils.getNowCal();
		
		cal.add(Calendar.DAY_OF_YEAR, -7);
		
		return cal.getTime();
	}
	
}
